<template>
  <div class="contBody">
    <h2 class="title">地图画线</h2>
    <div id="mapLine" class="map__x"></div>
    <div id="mousePosition" class="mousePosition"></div>
    <div class="btnGroup">
      <el-button type="primary" @click="startAnimation">
        {{ startButton }}
      </el-button>
    </div>
  </div>
</template>

<script>
import { Map, View, Feature } from "ol"; // 地图实例方法、视图方法
import Tile from "ol/layer/Tile"; // 瓦片渲染方法
import Vector from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { LineString, Point } from "ol/geom";
import { Style, Stroke, Icon } from "ol/style";
import { defaults as defaultControls, MousePosition } from "ol/control.js";
import { createStringXY } from "ol/coordinate";
import XYZ from "ol/source/XYZ";
import "ol/ol.css"; // 地图样式

import start from "@/assets/images/icon_start.png";
import end from "@/assets/images/icon_end.png";
import car from "@/assets/images/icon_car.png";

export default {
  name: "mapLine",
  data() {
    return {
      map: null,
      animating: false,
      speed: 10,
      now: undefined,
      startButton: "开始运动",
      lineListsProgress: [],
    };
  },
  mounted() {
    this.initMap();
  },
  methods: {
    initMap() {
      this.lineLists = [];
      for (let i = 0; i < 1200; i++) {
        const num = Math.sin((i / 360) * Math.PI);
        // 计算点位置
        const x = 114.2218 + i / 200;
        const y = 30.5695 + num;
        this.lineLists.push([x, y]);
      }

      //将离散点构建成一条折线
      const route = new LineString(this.lineLists);
      // const routeProgress = new LineString(lineLists.slice(0, 150));
      //获取直线的坐标
      this.routeCoords = route.getCoordinates();
      this.routeLength = this.routeCoords.length;
      this.center = [114.2905, 30.5607];

      this.routeFeature = new Feature({
        type: "route",
        geometry: route,
      });
      this.routeFeatureProgress = this.addProgress();
      this.geoMarker = new Feature({
        type: "geoMarker",
        geometry: new Point(this.routeCoords[0]),
      });
      this.startMarker = new Feature({
        type: "start",
        geometry: new Point(this.routeCoords[0]),
      });
      this.endMarker = new Feature({
        type: "end",
        geometry: new Point(this.routeCoords[this.routeLength - 1]),
      });

      this.styles = {
        route: new Style({
          stroke: new Stroke({
            width: 6,
            color: [237, 212, 0, 0.8],
          }),
        }),
        routep: new Style({
          stroke: new Stroke({
            width: 6,
            color: [255, 0, 0, 0.8],
          }),
        }),
        start: new Style({
          image: new Icon({
            anchor: [0.5, 0.8],
            src: start,
            scale: 0.4,
          }),
        }),
        end: new Style({
          image: new Icon({
            anchor: [0.5, 0.8],
            src: end,
            scale: 0.4,
          }),
        }),
        geoMarker: new Style({
          image: new Icon({
            src: car,
            scale: 0.2,
            anchor: [0, 1],
            // 设置旋转角度(传值)
            rotation: (360 * Math.PI) / 180,
          }),
        }),
      };

      this.vectorSourceLines = new VectorSource({
        features: [this.routeFeature],
      });
      this.vectorLines = new Vector({
        source: this.vectorSourceLines,
        style: (feature) => {
          //如果动画是激活的就隐藏geoMarker
          if (this.animating && feature.get("type") === "geoMarker") {
            return null;
          }
          return this.styles[feature.get("type")];
        },
      });

      this.vectorLayer = new Vector({
        source: new VectorSource({
          features: [this.geoMarker, this.startMarker, this.endMarker],
        }),
        style: (feature) => {
          //如果动画是激活的就隐藏geoMarker
          if (this.animating && feature.get("type") === "geoMarker") {
            return null;
          }
          return this.styles[feature.get("type")];
        },
      });

      // 地图实例
      this.map = new Map({
        target: "mapLine",
        loadTilesWhileAnimating: true,
        layers: [
          // 图层
          new Tile({
            source: new XYZ({
              url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7", // 新高德地图
            }),
            visible: true,
          }),
          this.vectorLines,
          this.vectorLayer,
        ],
        view: new View({
          projection: "EPSG:4326",
          center: this.center,
          minZoom: 2,
          maxZoom: 19,
          zoom: 8,
        }),
        controls: defaultControls({}).extend([
          new MousePosition({
            //坐标格式
            coordinateFormat: createStringXY(4),
            //地图投影坐标系（若未设置则输出为默认投影坐标系下的坐标）
            projection: "EPSG:4326",
            //坐标信息显示样式类名，默认是'ol-mouse-position'
            className: "mousePosition",
            //显示鼠标位置信息的目标容器
            target: document.getElementById("mousePosition"),
            //未定义坐标的标记
            undefinedHTML: "&nbsp;",
          }),
        ]),
      });

      this.vectorSourceLines.addFeature(this.routeFeatureProgress);
    },

    addProgress() {
      const routeProgress = new LineString(this.lineListsProgress);
      return new Feature({
        type: "routep",
        geometry: routeProgress,
      });
    },

    addSourceLineProgress() {
      // 先清空
      this.vectorSourceLines.removeFeature(this.routeFeatureProgress);

      // 再渲染
      this.routeFeatureProgress = this.addProgress();
      this.vectorSourceLines.addFeature(this.routeFeatureProgress);
    },

    moveFeature(event) {
      var vectorContext = event.vectorContext;
      var frameState = event.frameState;

      // 线覆盖

      if (this.animating) {
        var elapsedTime = frameState.time - this.now;
        //通过增加速度，来获得lineString坐标
        var index = Math.round((this.speed * elapsedTime) / 1000);

        if (index >= this.routeLength) {
          this.stopAnimation(true);
          return;
        }
        if (index > 0) {
          const prevPoint = new Point(this.routeCoords[index - 1]);
          const currentPoint = new Point(this.routeCoords[index]);

          this.lineListsProgress.push(currentPoint.flatCoordinates);
          this.addSourceLineProgress();

          this.rotation = this.setAngle(
            prevPoint.flatCoordinates,
            currentPoint.flatCoordinates
          );

          const feature = new Feature(currentPoint);
          vectorContext.drawFeature(feature, this.geoMarkerFun(this.rotation));
        }
      }
      //继续动画效果
      this.map.render();
    },

    geoMarkerFun(angle) {
      return new Style({
        image: new Icon({
          src: car,
          scale: 0.2,
          anchor: [0.5, 0.8],
          rotation: angle,
        }),
      });
    },

    startAnimation() {
      this.lineListsProgress = [];
      this.addSourceLineProgress();

      if (this.animating) {
        this.stopAnimation(false);
      } else {
        this.animating = true;
        this.now = new Date().getTime();
        this.startButton = "结束运动";
        //隐藏geoMarker
        this.geoMarker.setStyle(null);
        //设置显示范围
        this.map.getView().setCenter(this.center);
        this.map.on("postcompose", this.moveFeature);
        this.map.render();
      }
    },

    stopAnimation(ended) {
      this.animating = false;
      this.startButton = "开始运动";

      //如果动画取消就开始动画
      var coord = ended
        ? this.routeCoords[this.routeLength - 1]
        : this.routeCoords[0];
      /** @type {ol.geom.Point} */
      (this.geoMarker.getGeometry()).setCoordinates(coord);
      //移除监听
      this.map.un("postcompose", this.moveFeature);
    },

    setAngle(first, second) {
      let y = second[1] - first[1];
      let x = second[0] - first[0];
      let radAngle = Math.atan(y / x);
      if (y <= 0 && x >= 0) {
        //第二象限
        radAngle = -radAngle;
      } else if (x >= 0 && y >= 0) {
        //第一象限
        radAngle = -radAngle;
      } else if (x <= 0 && y >= 0) {
        //第四象限
        radAngle = Math.PI - radAngle;
      } else if (x <= 0 && y <= 0) {
        //第三象限
        radAngle = Math.PI - radAngle;
      }

      return radAngle;
    },
  },
};
</script>

<style lang="css" scoped>
.contBody .title {
  padding: 0 0 24px;
}
.map__x {
  width: 100%;
  height: 600px;
  border: 1px solid #eee;
}
.btnGroup {
  text-align: left;
}

.mousePosition {
  text-align: left;
  color: rgb(0, 0, 0);
  font-size: 16px;
  font-family: "微软雅黑";
}
</style>
